#include "album_video.h"
#include "file_utils.h"
#include "elog.h"
#include "elog_file.h"
#include "album_common.h"
#include "img_utils.h"

#include <stdlib.h>

extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

#include <string>
#include <filesystem>

namespace fs = std::filesystem;
using namespace std;

static sqlite3* m_db = nullptr;
static string video_table_name = "videoInfo";
static std::string video_folder_path = album_path + video_path;


AlbumVideo::AlbumVideo() {

}

AlbumVideo::~AlbumVideo() {

}

// 使用FFmpeg获取视频信息
int AlbumVideo::getVideoInfo(const std::string& file_path, VideoInfo& video_info) {
    av_register_all();
    AVFormatContext* format_context = nullptr;

    // 打开视频文件
    if (avformat_open_input(&format_context, file_path.c_str(), nullptr, nullptr) != 0) {
        log_e("can not open video file: %s", file_path.c_str());
        return -1;
    }

    // 获取视频信息
    if (avformat_find_stream_info(format_context, nullptr) < 0) {
        log_e("can not get video info");
        avformat_close_input(&format_context);
        return -1;
    }

    // 寻找视频流
    AVStream* video_stream = nullptr;
    for (unsigned int i = 0; i < format_context->nb_streams; i++) {
        if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream = format_context->streams[i];
            break;
        }
    }

    if (!video_stream) {
        log_e("can not find video stream");
        avformat_close_input(&format_context);
        return -1;
    }

    // 获取视频分辨率
    int video_width = video_stream->codecpar->width;
    int video_height = video_stream->codecpar->height;
    video_info.unique_info.width = video_width;
    video_info.unique_info.height = video_height;
    log_i("video width = %d, height = %d", video_width, video_height);

    // 获取帧率
    AVRational frame_rate = video_stream->avg_frame_rate;
    double frame_rate_value = av_q2d(frame_rate);
    log_i("video frame rate = %f fps", frame_rate_value);

    // 获取视频时长（单位：秒）
    int64_t duration = format_context->duration;
    int duration_in_seconds = static_cast<int>(duration / AV_TIME_BASE);
    video_info.unique_info.duration = duration_in_seconds;
    log_i("video duration :%d", duration_in_seconds);

    avformat_close_input(&format_context);

    return 0;
}

// 获取视频第一帧
void AlbumVideo::getFrameFromvideo(const std::string& video_file, const std::string& output_image_file) {
    if (!fs::exists(output_image_file)) {
        string ffmpeg_command = "ffmpeg -i " + video_file + " -vframes 1 " + output_image_file;
        system(ffmpeg_command.c_str());
        system("sync");
    } else {
        log_e("file exists");
    }
}

int AlbumVideo::createVideoInfoTable(sqlite3* db) {
    char* errMsg = 0;
    int rc;

    m_db = db;
    // 创建videoInfo表（如果还未创建）
    const char* createTableSQL = R"(
        CREATE TABLE IF NOT EXISTS videoInfo (
            id INTEGER PRIMARY KEY,
            file_path TEXT UNIQUE,
            modification_time INTEGER,
            thumbnail_path TEXT,
            file_size INTEGER,
            duration INTEGER,
            width INTEGER,
            height INTEGER,
            media_type INTEGER
        );
    )";

    rc = sqlite3_exec(db, createTableSQL, NULL, 0, &errMsg);
    if (rc != SQLITE_OK) {
        log_e("cannot create table: %s", errMsg);
        sqlite3_free(errMsg);
    } else {
        log_i("create table successfully");
    }

    return 0;
}

// 插入视频信息
int AlbumVideo::insertVideoInfo(const VideoInfo& video_info) {
    log_i("insertVideoInfo file_path = %s", video_info.common_info.file_path.c_str());
    if (!m_db) {
        log_e("The database connection is not initialized");
        return -1;
    }

    std::string insertDataSQL = "INSERT INTO " + video_table_name + " (file_path, modification_time, thumbnail_path, file_size, duration, width, height, media_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?);";

    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(m_db, insertDataSQL.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return -1;
    }

    sqlite3_bind_text(stmt, 1, video_info.common_info.file_path.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 2, video_info.common_info.modification_time);
    sqlite3_bind_text(stmt, 3, video_info.common_info.thumbnail_path.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 4, video_info.common_info.file_size);
    sqlite3_bind_int(stmt, 5, video_info.unique_info.duration);
    sqlite3_bind_int(stmt, 6, video_info.unique_info.width);
    sqlite3_bind_int(stmt, 7, video_info.unique_info.height);
    sqlite3_bind_int(stmt, 8, video_info.common_info.media_type);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not insert :%s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return -1;
    }

    sqlite3_finalize(stmt);

    return 0;
}

int AlbumVideo::getCount() {
    int count = 0;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return count;
    }

    std::string sql = "SELECT COUNT(*) FROM " + video_table_name + ";";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return count;
    }

    if (sqlite3_step(stmt) == SQLITE_ROW) {
        count = sqlite3_column_int(stmt, 0);
    }

    sqlite3_finalize(stmt);

    return count;
}

// 查询视频信息并按照修改时间排序
std::vector<VideoInfo> AlbumVideo::queryVideoInfoByTime() {
    std::vector<VideoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + video_table_name + " ORDER BY modification_time DESC;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        VideoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.duration = sqlite3_column_int(stmt, 5);
        info.unique_info.width = sqlite3_column_int(stmt, 6);
        info.unique_info.height = sqlite3_column_int(stmt, 7);
        info.common_info.media_type = sqlite3_column_int(stmt, 8);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

std::vector<VideoInfo> AlbumVideo::queryVideoInfoByTime(int page_index, int page_size) {
    std::vector<VideoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + video_table_name + " ORDER BY modification_time DESC LIMIT ? OFFSET ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    int offset = page_index * page_size;
    sqlite3_bind_int(stmt, 1, page_size);
    sqlite3_bind_int(stmt, 2, offset);

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        VideoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.duration = sqlite3_column_int(stmt, 5);
        info.unique_info.width = sqlite3_column_int(stmt, 6);
        info.unique_info.height = sqlite3_column_int(stmt, 7);
        info.common_info.media_type = sqlite3_column_int(stmt, 8);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}


// 查询所有视频信息
std::vector<VideoInfo> AlbumVideo::queryAllVideoInfo() {
    std::vector<VideoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + video_table_name + ";";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        VideoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.duration = sqlite3_column_int(stmt, 5);
        info.unique_info.width = sqlite3_column_int(stmt, 6);
        info.unique_info.height = sqlite3_column_int(stmt, 7);
        info.common_info.media_type = sqlite3_column_int(stmt, 8);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

// 查询指定文件路径的视频信息
std::vector<VideoInfo> AlbumVideo::queryVideoInfoByFilepath(const std::string& file_path) {
    std::vector<VideoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + video_table_name + " WHERE file_path = ?";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    sqlite3_bind_text(stmt, 1, file_path.c_str(), -1, SQLITE_STATIC);

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        VideoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.duration = sqlite3_column_int(stmt, 5);
        info.unique_info.width = sqlite3_column_int(stmt, 6);
        info.unique_info.height = sqlite3_column_int(stmt, 7);
        info.common_info.media_type = sqlite3_column_int(stmt, 8);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

// 修改视频信息
bool AlbumVideo::updateVideoInfo(const std::string& file_path, const std::string& new_file_path) {
    if (!m_db) {
        log_e("The database connection is not initialized");
        return false;
    }

    std::string sql = "UPDATE " + video_table_name + " SET file_path = ? WHERE file_path = ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return false;
    }

    sqlite3_bind_text(stmt, 1, new_file_path.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, file_path.c_str(), -1, SQLITE_STATIC);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not update: %s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return false;
    }

    sqlite3_finalize(stmt);

    return true;
}

// 删除视频信息
bool AlbumVideo::deleteVideoInfo(const std::string& file_path) {
    log_i("deleteVideoInfo file_path = %s", file_path.c_str());

    if (!m_db) {
        log_e("The database connection is not initialized");
        return false;
    }

    std::string sql = "DELETE FROM " + video_table_name + " WHERE file_path = ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return false;
    }

    sqlite3_bind_text(stmt, 1, file_path.c_str(), -1, SQLITE_STATIC);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not delete: %s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return false;
    }

    sqlite3_finalize(stmt);

    return true;
}

void AlbumVideo::printVideoInfo(const VideoInfo& video_info) {
    log_i("video_info file_path = %s", video_info.common_info.file_path.c_str());
    log_i("video_info modification_time = %ld", video_info.common_info.modification_time);
    log_i("video_info thumbnail_path = %s", video_info.common_info.thumbnail_path.c_str());
    log_i("video_info file_size = %d", video_info.common_info.file_size);
    log_i("video_info duration = %d", video_info.unique_info.duration);
    log_i("video_info width = %d", video_info.unique_info.width);
    log_i("video_info height = %d", video_info.unique_info.height);
}

bool AlbumVideo::deleteVideo(const std::string& file_path) {
    if (file_path != "" && isPathContains(file_path, video_folder_path) && file_path.size() > video_folder_path.size()) {
        fs::path video_file_path(file_path);
        string img_thumbnail_path = video_folder_path + video_thumbnail_path + video_file_path.stem().string() + ".jpg";
        if (deleteVideoInfo(file_path) && deleteFile(file_path) && deleteFile(img_thumbnail_path)) {
            return true;
        } else {
            return false;
        }
    }

    return false;
}

bool AlbumVideo::renameVideo(const std::string& file_path, const std::string& new_filename) {
    if (file_path != "" && isPathContains(file_path, video_folder_path) && file_path.size() > video_folder_path.size()) {
        std::string extension = getFileExtension(file_path);
        fs::path video_file_path(file_path);
        std::string img_thumbnail_path = video_folder_path + video_thumbnail_path + video_file_path.stem().string() + ".jpg";
        std::string new_file_path = video_folder_path + new_filename + extension;
        std::string new_thumbnail_path = video_folder_path + video_thumbnail_path + new_filename + ".jpg";
        if (renameFile(file_path, new_file_path)) {
            renameFile(img_thumbnail_path, new_thumbnail_path);
            updateVideoInfo(file_path, new_file_path);
            return true;
        } else {
            return false;
        }
    }

    return false;
}

// 拍完视频后调用
bool AlbumVideo::updateVideo(const std::string& file_path) {
    log_i("updateVideo file_path = %s", file_path.c_str());
    if (file_path != "" && isPathContains(file_path, video_folder_path) && file_path.size() > video_folder_path.size()) {
        VideoInfo video_info;
        fs::path video_file_path(file_path);
        string img_thumbnail_path = video_folder_path + video_thumbnail_path + video_file_path.stem().string() + ".jpg";
        string img_thumbnail_temp_path = video_folder_path + video_thumbnail_path + video_file_path.stem().string() + "_temp.jpg";

        if (!fs::exists(img_thumbnail_path)) {
            getFrameFromvideo(file_path, img_thumbnail_temp_path);
        }

        if (fs::exists(img_thumbnail_temp_path)) {
            imageDownsample(img_thumbnail_temp_path, img_thumbnail_path, album_thumbnail_width, album_thumbnail_height);
            deleteFile(img_thumbnail_temp_path);
        }
          
        getVideoInfo(file_path, video_info);
        video_info.common_info.file_size = getFileSizeIntUsingStat64(file_path);
        video_info.common_info.modification_time = getFileModifiedTimeUsingStat64(file_path);
        video_info.common_info.file_path = file_path;
        video_info.common_info.thumbnail_path = img_thumbnail_path;
        video_info.common_info.media_type = MEDIA_VIDEO;
        video_info.common_info.width = 3840;
        video_info.common_info.height = 2160;

        insertVideoInfo(video_info);

        fileSync();
        
        return true;
    }

    return false;
}
